/* 函数重载 */
// 可以通过写重载签名 (overlaod signatures) 说明一个函数的不同调用方法。 
// 我们需要写一些函数签名 (通常两个或者更多)，然后再写函数体的内容
function makeDate(timestamp: number): Date; // 重载签名
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) { // 实现签名
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
// Error：No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.ts(2575)
const d3 = makeDate(1, 3);

// 重载签名和实现签名=============================================================================================
function fn(x: string): void; // 重载签名
// function fn(): void; // 重载签名
function fn() { // 实现签名
  // ...
}
// Expected 1 arguments, but got 0.ts(2554)
fn();

// 实现签名必须和重载签名必须兼容（compatible）
function fn17(x: boolean): void;
// Argument type isn't right
function fn17(x: string): void;
// This overload signature is not compatible with its implementation signature.
function fn17(x: boolean) {}

function fn18(x: string): string;
// Return type isn't right
function fn18(x: number): boolean;
// This overload signature is not compatible with its implementation signature.
function fn18(x: string | number) {
  return "oops";
}

// 写一个好的函数重载的一些建议===================================================================
// 写一个函数：返回数组或者字符串的长度
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
  return x.length;
}

len(""); // OK
len([0]); // OK
// 不可以传入一个可能是字符串或者数组的值，因为TS只能一次用一个函数重载处理一次函数调用
len(Math.random() > 0.5 ? "hello" : [0]);

// 替代方案：使用联合类型
// 尽可能使用联合类型来替代重载
function len2(x: string | any[]) {
  return x.length;
}
len2(""); // OK
len2([0]); // OK
// 不可以传入一个可能是字符串或者数组的值，因为TS只能一次用一个函数重载处理一次函数调用
len2(Math.random() > 0.5 ? "hello" : [0]);

// 在函数中声明this
const user47 = {
  id: 123,
  admin: false,
  becomeAdmin: function() {
    // this只想外层的user47对象
    this.admin = true;
  }
}

// TS允许在函数体内声明this的类型
type User = { id: number; admin: boolean; }
interface DB {
  filterUsers(filter: (this: User) => boolean): User[];
}
function getDB(): DB {
  return {
    filterUsers: function (filter: (this: User) => boolean) {
      return [
        { id: 1, admin: true },
        { id: 2, admin: false },
        { id: 3, admin: true }
      ].filter(filter);
    }
  }
}
const db = getDB();
const admins = db.filterUsers(function (this: User) {
  return this.admin;
});
const admins47 = db.filterUsers(() => this.admin);
